package com.shxzz.easy12306.framework.starter.idempotent.core;

import com.shxzz.easy12306.framework.starter.idempotent.annotation.Idempotent;
import com.shxzz.easy12306.framework.starter.idempotent.core.exception.MessageRepeatConsumptionException;
import com.shxzz.easy12306.framework.starter.idempotent.core.handler.IdempotentExecuteHandler;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

/**
 * 幂等逻辑切面
 *
 * @author shxzz
 * @create 2024-02-22-21:51
 */
@Slf4j
@Aspect
public class IdempotentAspect {

    @Around("@annotation(com.shxzz.easy12306.framework.starter.idempotent.annotation.Idempotent)")
    public Object idempotentHandler(ProceedingJoinPoint joinPoint) throws Throwable {
        Object resultObj = null;

        Idempotent idempotent = getIdempotent(joinPoint);
        IdempotentExecuteHandler idempotentExecuteHandler = IdempotentExecuteHandlerFactory.getInstance(idempotent.scene(), idempotent.type());
        try {
            idempotentExecuteHandler.execute(joinPoint, idempotent);
            resultObj = joinPoint.proceed();
            idempotentExecuteHandler.postProcessing();
        } catch (MessageRepeatConsumptionException ex) {
            if(!ex.isMessageConsumed()) {
                log.error("消息重复消费，重复信息为: {}", ex.getRepeatMessageKey());
                throw ex;
            }
            return null;
        } catch (Exception e) {
            idempotentExecuteHandler.exceptionProcessing();
            throw e;
        } finally {
            IdempotentContext.remove();
        }
        return resultObj;
    }

    public static Idempotent getIdempotent(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = joinPoint.getTarget().getClass().getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        return targetMethod.getAnnotation(Idempotent.class);
    }
}
